-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-- 
-- PostGIS - Spatial Types for PostgreSQL
-- http://postgis.refractions.net
--
-- Copyright (C) 2011 Andrea Peri <aperi2007@gmail.com>
--
-- This is free software; you can redistribute and/or modify it under
-- the terms of the GNU General Public Licence. See the COPYING file.
--
-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

--{
--
-- Andrea Peri (27 Feb 2011) creation
--
-- GetFaceByPoint(atopology, point, tol)
--
-- Retrieve a Face ID given a POINT and a tolerance
-- tolerance = 0 mean exactly intersection
--
-- Returns return the integer ID if there is a face on the Point.
--
-- When the Point is even a Node it raise an exception. 
-- This case is testable with the GetNodeByPoint(atopology, apoint, tol)
--
-- If there isn't any face in the Point, GetFaceByPoint return 0.
--
-- if near the point there are two or more faces it throw an exception.
--
CREATE OR REPLACE FUNCTION topology.GetFaceByPoint(atopology varchar, apoint geometry, tol1 float8)
	RETURNS int
AS
$$
DECLARE
	sql text;
	idface int;
BEGIN

    idface := -1;

	--
	-- Atopology and apoint are required
	-- 
	IF atopology IS NULL OR apoint IS NULL THEN
		RAISE EXCEPTION 'Invalid null argument';
	END IF;

	--
	-- Apoint must be a point
	--
	IF substring(geometrytype(apoint), 1, 5) != 'POINT'
	THEN
		RAISE EXCEPTION 'Node geometry must be a point';
	END IF;

	--
	-- Tolerance must be >= 0
	--
	IF tol1 < 0
	THEN
		RAISE EXCEPTION 'Tolerance must be >=0';
	END IF;
    --
    -- first test is to check if there is inside an mbr
    --
    if tol1 = 0 then
    	sql := 'SELECT a.face_id FROM ' 
        || quote_ident(atopology) 
        || '.face as a WHERE '
        || '(a.mbr && ' || quote_literal(apoint::text)||'::geometry) '
        || 'LIMIT 1;';
    else
    	sql := 'SELECT a.face_id FROM ' 
        || quote_ident(atopology) 
        || '.face as a WHERE '
        || '(ST_DWithin(a.mbr,' || quote_literal(apoint::text)||'::geometry,' || tol1::text || ') ) '
        || 'LIMIT 1;';
    end if;

    BEGIN
    EXECUTE sql INTO STRICT idface;
    EXCEPTION
        WHEN NO_DATA_FOUND THEN
            idface = 0;
    END;

    if idface > 0 then
        --
        -- probably there is something so now check the exact test
        --

    	if tol1 = 0 then
        	sql := 'SELECT e.face_id FROM ('
            || 'SELECT d.face_id,ST_BuildArea(ST_Union(geom)) as geom FROM ('
    		|| 'SELECT b.edge_id as edge_id,b.left_face as face_id,b.geom as geom FROM '
        	|| quote_ident(atopology) || '.edge_data as b,'
            || '(SELECT a.face_id FROM '
    		|| quote_ident(atopology) || '.face as a '
        	|| 'WHERE ST_Intersects(a.mbr,' || quote_literal(apoint::text)||'::geometry)=true'
            || ') as c '
    		|| 'WHERE (b.left_face = c.face_id) '
        	|| ' UNION ALL '
            || 'SELECT b.edge_id as edge_id, b.right_face as face_id, b.geom as geom FROM '
    		|| quote_ident(atopology) || '.edge_data as b,'
        	|| '(SELECT a.face_id FROM '
            || quote_ident(atopology) || '.face as a '
    		|| 'WHERE ST_Intersects(a.mbr,' || quote_literal(apoint::text)||'::geometry)=true'
        	|| ') as c '
            || 'WHERE (b.right_face = c.face_id) '
    		|| ') as d '
        	|| 'GROUP BY face_id '
            || ') as e '
    		|| 'WHERE ST_Intersects(e.geom, ' || quote_literal(apoint::text)||'::geometry)=true;';
        else
        	sql := 'SELECT e.face_id FROM ('
            || 'SELECT d.face_id,ST_BuildArea(ST_Union(geom)) as geom FROM ('
    		|| 'SELECT b.edge_id as edge_id,b.left_face as face_id,b.geom as geom FROM '
        	|| quote_ident(atopology) || '.edge_data as b,'
            || '(SELECT a.face_id FROM '
    		|| quote_ident(atopology) || '.face as a '
        	|| 'WHERE ST_DWithin(a.mbr,' || quote_literal(apoint::text)||'::geometry,' || tol1::text || ')=true'
            || ') as c '
    		|| 'WHERE (b.left_face = c.face_id) '
        	|| ' UNION ALL '
            || 'SELECT b.edge_id as edge_id, b.right_face as face_id, b.geom as geom FROM '
    		|| quote_ident(atopology) || '.edge_data as b,'
        	|| '(SELECT a.face_id FROM '
            || quote_ident(atopology) || '.face as a '
    		|| 'WHERE ST_DWithin(a.mbr,' || quote_literal(apoint::text)||'::geometry,' || tol1::text || ')=true'
        	|| ') as c '
            || 'WHERE (b.right_face = c.face_id) '
    		|| ') as d '
        	|| 'GROUP BY face_id '
            || ') as e '
    		|| 'WHERE ST_DWithin(e.geom, ' || quote_literal(apoint::text)||'::geometry,' || tol1::text || ')=true;';
        end if;
	
    	RAISE DEBUG ' ==> %',sql;

        BEGIN
            EXECUTE sql INTO STRICT idface;
        	EXCEPTION
        	    WHEN NO_DATA_FOUND THEN
                    idface = 0;
                WHEN TOO_MANY_ROWS THEN
                    RAISE EXCEPTION 'Two or more faces found';
            END;

    end if;
    
    RETURN idface;
	
END
$$
LANGUAGE 'plpgsql' STABLE STRICT;
--} GetFaceByPoint

